package com.harmontronics.hanma.paas.paas.domain.app.service.tools;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Tuple;
import com.harmontronics.hanma.paas.paas.consts.DbConstants;
import com.harmontronics.hanma.paas.paas.consts.EnvEnum;
import com.harmontronics.hanma.paas.paas.domain.app.entity.HttpServiceSetting;
import com.harmontronics.hanma.paas.paas.domain.app.entity.HttpTokenCache;
import com.harmontronics.hanma.paas.paas.domain.app.entity.valueobject.AuthTypeEnum;
import com.harmontronics.hanma.paas.paas.domain.app.entity.valueobject.I18nKeyEnum;
import com.harmontronics.hanma.paas.paas.domain.app.repository.persistence.HmHttpServiceRepository;
import com.harmontronics.hanma.paas.paas.domain.app.repository.persistence.HttpServiceSettingRepository;
import com.harmontronics.hanma.paas.paas.domain.app.repository.po.HmHttpServicePO;
import com.harmontronics.hanma.paas.paas.domain.app.repository.po.HttpServiceSettingPO;
import com.harmontronics.hanma.paas.paas.exception.ErrorKeyException;
import com.harmontronics.hanma.paas.paas.utils.OkHttpUtils;
import com.harmontronics.hanma.paas.paas.utils.RequestUtils;
import com.jayway.jsonpath.JsonPath;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.StringJoiner;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author ldb
 */
@Service
public class HttpServiceHelper {

    private static final Map<String, HttpTokenCache> HTTP_TOKEN_CACHE_MAP = new ConcurrentHashMap<>();

    @Autowired
    private HmHttpServiceRepository hmHttpServiceRepository;
    @Autowired
    private HttpServiceSettingRepository httpServiceSettingRepository;

    @Value("${app.env:DEV}")
    private String env;

    @Autowired
    private OkHttpClient okHttpClient;


    public String getToken(String serviceKey) {
        String appUnique = RequestUtils.getAppUnique();
        String keyPrefix = appUnique == null ? DbConstants.DS_MASTER_NAME : appUnique.split("_")[1];
        HttpTokenCache cache = HTTP_TOKEN_CACHE_MAP.get(keyPrefix + ":" + serviceKey);
        if (cache != null) {
            return cache.getToken();
        }
        return null;
    }


    public String getReqUrl(String serviceKey, String reqPath) {
        HmHttpServicePO servicePO = hmHttpServiceRepository.findByKey(serviceKey);
        HttpServiceSettingPO settingPO = httpServiceSettingRepository.findByServiceIdAndEnv(servicePO.getId(), Enum.valueOf(EnvEnum.class, env));
        return getReqUrl(servicePO, settingPO, reqPath);
    }

    /**
     * 获取请求地址  会加上鉴权信息  TOKEN检验会刷新缓存
     *
     * @param servicePO 服务
     * @param settingPO 鉴权相关配置
     * @param reqPath   请求路径
     * @return
     */
    public String getReqUrl(HmHttpServicePO servicePO, HttpServiceSettingPO settingPO, String reqPath) {
        if (servicePO.getAuthType() == AuthTypeEnum.NONE) {
            return concatUrl(settingPO.getServerUrl(), reqPath);
        }
        if (servicePO.getAuthType() == AuthTypeEnum.PASS_THROUGH) {
            String queryStr = getQueryStr(settingPO);
            String url = concatUrl(settingPO.getServerUrl(), reqPath);
            if (url.contains("?")) {
                return url + "&" + queryStr;
            } else {
                return url + "?" + queryStr;
            }
        }
        if (servicePO.getAuthType() == AuthTypeEnum.MOS) {
            String queryStr = getMosQueryStr(settingPO);
            String url = concatUrl(settingPO.getServerUrl(), reqPath);
            if (url.contains("?")) {
                return url + "&" + queryStr;
            } else {
                return url + "?" + queryStr;
            }

        }

        String appUnique = RequestUtils.getAppUnique();

        String keyPrefix = appUnique == null ? DbConstants.DS_MASTER_NAME : appUnique.split("_")[1];

        if (servicePO.getAuthType() == AuthTypeEnum.TOKEN) {
            HttpTokenCache cache = HTTP_TOKEN_CACHE_MAP.get(keyPrefix + ":" + servicePO.getKey());
            //查询到缓存 且未过期
            if (cache != null && cache.getExpireAt() > System.currentTimeMillis()) {
                return concatUrl(settingPO.getServerUrl(), reqPath);
            }
            if (cache != null) {
                HTTP_TOKEN_CACHE_MAP.remove(keyPrefix + ":" + servicePO.getKey());
            }
            //未查到缓存或已过期
            synchronized (settingPO.getHttpServiceId()) {
                HttpTokenCache retryCache = HTTP_TOKEN_CACHE_MAP.get(keyPrefix + ":" + servicePO.getKey());
                //查询到缓存 且未过期
                if (retryCache != null && retryCache.getExpireAt() > System.currentTimeMillis()) {
                    return concatUrl(settingPO.getServerUrl(), reqPath);
                }
                try {
                    String queryStr = getQueryStr(settingPO);
                    String url = settingPO.getTokenUrl();
                    String reqUrl = url + "?" + queryStr;
                    Request.Builder builder = new Request.Builder();
                    builder.url(reqUrl).get();
                    Request request = builder.build();
                    String responseStr = OkHttpUtils.execNewCall(okHttpClient, request);
                    String token = null;
                    if ("$".equals(settingPO.getTokenPath())) {
                        token = responseStr;
                    } else {
                        token = JsonPath.read(responseStr, settingPO.getTokenPath());
                    }
                    long expiredAr = System.currentTimeMillis() + 1000L * 60 * settingPO.getTokenExpire();
                    HTTP_TOKEN_CACHE_MAP.put(keyPrefix + ":" + servicePO.getKey(), new HttpTokenCache(token, expiredAr));
                } catch (IOException e) {
                    throw new ErrorKeyException(I18nKeyEnum.GET_TOKEN_ERROR);
                }
            }

        }

        throw new IllegalArgumentException();
    }

    private String getMosQueryStr(HttpServiceSettingPO po) {
        Tuple tuple = BeanUtil.toBean(po, HttpServiceSetting.class).getMosParamAndCheck();
        String token = tuple.get(2);
        String nonceStr = String.valueOf(System.currentTimeMillis());
        if (StringUtils.hasLength(token)) {
            nonceStr = token;
        }
        String str = tuple.get(0) + nonceStr + tuple.get(1);
        String signStr = DigestUtils.sha1Hex(str);
        StringJoiner joiner = new StringJoiner("&");
        joiner.add("appId=" + tuple.get(0));
        joiner.add("nonce=" + nonceStr);
        joiner.add("sign=" + signStr);
        return joiner.toString();
    }

    private String concatUrl(String serverUrl, String path) {
        String pathSeparator = "/";
        if (serverUrl.endsWith(pathSeparator) && path.startsWith(pathSeparator)) {
            return serverUrl.substring(serverUrl.length() - 1) + path;
        }
        if (!serverUrl.endsWith(pathSeparator) && !path.startsWith(pathSeparator)) {
            return serverUrl + pathSeparator + path;
        }
        return serverUrl + path;
    }

    private String getQueryStr(HttpServiceSettingPO po) {
        List<Map<String, String>> params = BeanUtil.toBean(po, HttpServiceSetting.class).paramJson2List();
        return toQueryStr(params);
    }

    public String toQueryStr(List<Map<String, String>> params) {
        StringJoiner joiner = new StringJoiner("&");
        for (Map<String, String> paramMap : params) {
            String name = paramMap.get("name");
            String value = paramMap.get("value");
            joiner.add(name + "=" + value);
        }
        return joiner.toString();
    }

}
